﻿using Potential_Software.Win32;
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

namespace Abide.Forms
{
    public partial class AbideNotification : Form
    {
        private readonly float transitionIn;
        private readonly float transitionOut;
        private readonly float hangTime;

        private PointF changeF = PointF.Empty;
        private PointF startF = PointF.Empty;
        private volatile bool close = false;
        private volatile bool closing = false;

        private System.Windows.Forms.Timer timer;

        //Layered Form
        protected override System.Windows.Forms.CreateParams CreateParams
        {
            get
            {
                if (DesignMode)
                    return base.CreateParams;

                System.Windows.Forms.CreateParams Params = base.CreateParams;
                Params.ExStyle = (int)(WinUser.WS_EX_LAYERED);
                return Params;
            }
        }

        //Initializer
        public AbideNotification()
        {
            //Set Timings
            transitionIn = 500;
            transitionOut = 500;
            hangTime = 3000;

            //Set Size
            this.Size = new Size(90, 48);
            this.Text = "Empty Notification";

            //Initialize
            InitializeComponent();
        }
        public AbideNotification(string NotificationText)
        {
            //Set Timings
            transitionIn = 500;
            transitionOut = 500;
            hangTime = 3000;

            //Set Size
            this.Size = new Size(90, 48) + TextRenderer.MeasureText(NotificationText, Font);
            this.Text = NotificationText;

            //Initialize
            InitializeComponent();
        }
        public AbideNotification(string NotificationText, int Duration)
        {
            //Set Timings
            transitionIn = 500;
            transitionOut = 500;
            hangTime = Duration;

            //Set Size
            this.Size = new Size(90, 48) + TextRenderer.MeasureText(NotificationText, Font);
            this.Text = NotificationText;

            //Initialize
            InitializeComponent();
        }
        private void InitializeComponent()
        {
            // timer //
            this.timer = new System.Windows.Forms.Timer();
            this.timer.Interval = 1;
            this.timer.Tick += timer_Tick;

            // this //
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            this.StartPosition = FormStartPosition.Manual;
            this.Location = new Point(Screen.PrimaryScreen.Bounds.Width - Width, Screen.PrimaryScreen.Bounds.Height);
            this.ShowInTaskbar = false;
            this.TopMost = true;
            this.Shown += AbideNotification_Shown;
            this.FormClosing += AbideNotification_FormClosing;
            this.Click += AbideNotification_Click;
        }

        //Form Events
        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            base.OnPaint(e);

            System.Drawing.Bitmap FormBitm = new System.Drawing.Bitmap(Width, Height);

            using (System.Drawing.Graphics imgGfx = System.Drawing.Graphics.FromImage(FormBitm))
            {
                using (System.Windows.Forms.PaintEventArgs pe = new System.Windows.Forms.PaintEventArgs(imgGfx, new System.Drawing.Rectangle(0, 0, Width, Height)))
                {
                    OnPaintLayeredWindow(pe);
                }
            }

            System.IntPtr screenDc = User32.GetDC(System.IntPtr.Zero);
            System.IntPtr memDc = Gdi32.CreateCompatibleDC(screenDc);
            System.IntPtr hBitmap = System.IntPtr.Zero;
            System.IntPtr oldBitmap = System.IntPtr.Zero;

            try
            {
                hBitmap = FormBitm.GetHbitmap(System.Drawing.Color.FromArgb(0x0));
                oldBitmap = Gdi32.SelectObject(memDc, hBitmap);

                SIZE size = new SIZE(); size.cx = FormBitm.Width; size.cy = FormBitm.Height;
                POINT pointSource = new POINT(); pointSource.x = 0; pointSource.y = 0;
                POINT topPos = new POINT(); topPos.x = Left; topPos.y = Top;

                BLENDFUNCTION blend = new BLENDFUNCTION();
                blend.BlendOp = WinUser.AC_SRC_OVER;
                blend.BlendFlags = 0x0;
                blend.SourceConstantAlpha = 0xFF;
                blend.AlphaFormat = WinUser.AC_SRC_ALPHA;

                User32.UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, WinUser.ULW_ALPHA);
            }
            finally
            {
                User32.ReleaseDC(System.IntPtr.Zero, screenDc);

                if (hBitmap != System.IntPtr.Zero)
                {
                    Gdi32.SelectObject(memDc, oldBitmap);
                    Gdi32.DeleteObject(hBitmap);
                }

                Gdi32.DeleteDC(memDc);
                FormBitm.Dispose();
            }
        }
        protected virtual void OnPaintLayeredWindow(System.Windows.Forms.PaintEventArgs e)
        {
            //Calculate extra width and height
            int ExtraX = Size.Width - 90;
            int ExtraY = Size.Height - 45;

            //Get Images for Tiling
            Bitmap Top = new Bitmap(1, 37); Bitmap Bottom = new Bitmap(1, 9); Bitmap Left = new Bitmap(10, 1);
            Bitmap Right = new Bitmap(11, 1); Bitmap Center = new Bitmap(1, 1);
            using(Graphics g = Graphics.FromImage(Top))
                g.DrawImage(Properties.Resources.Notification, new Rectangle(0, 0, 1, 37), new Rectangle(78, 0, 1, 37), GraphicsUnit.Pixel);
            using (Graphics g = Graphics.FromImage(Bottom))
                g.DrawImage(Properties.Resources.Notification, new Rectangle(0, 0, 1, 9), new Rectangle(78, 39, 1, 9), GraphicsUnit.Pixel);
            using (Graphics g = Graphics.FromImage(Left))
                g.DrawImage(Properties.Resources.Notification, new Rectangle(0, 0, 10, 1), new Rectangle(0, 37, 10, 1), GraphicsUnit.Pixel);
            using (Graphics g = Graphics.FromImage(Right))
                g.DrawImage(Properties.Resources.Notification, new Rectangle(0, 0, 11, 1), new Rectangle(78, 37, 11, 1), GraphicsUnit.Pixel);
            using (Graphics g = Graphics.FromImage(Center))
                g.DrawImage(Properties.Resources.Notification, new Rectangle(0, 0, 1, 1), new Rectangle(78, 37, 1, 1), GraphicsUnit.Pixel);

            //Create Texture Brushes
            TextureBrush TopBrush, LeftBrush, RightBrush, BottomBrush, CenterBrush;
            TopBrush = new TextureBrush(Top, System.Drawing.Drawing2D.WrapMode.TileFlipX);
            LeftBrush = new TextureBrush(Left, System.Drawing.Drawing2D.WrapMode.TileFlipY);
            RightBrush = new TextureBrush(Right, System.Drawing.Drawing2D.WrapMode.TileFlipY);
            BottomBrush = new TextureBrush(Bottom, System.Drawing.Drawing2D.WrapMode.TileFlipX);
            CenterBrush = new TextureBrush(Center);

            //Clear Background
            e.Graphics.Clear(Color.Transparent);

            //Draw Corners
            e.Graphics.DrawImage(Properties.Resources.Notification, new Rectangle(0, 0, 78, 37), new Rectangle(0, 0, 78, 37), GraphicsUnit.Pixel);
            e.Graphics.DrawImage(Properties.Resources.Notification, new Rectangle(0, Height - 9, 10, 9), new Rectangle(0, 39, 10, 9), GraphicsUnit.Pixel);
            e.Graphics.DrawImage(Properties.Resources.Notification, new Rectangle(Width - 10, 0, 10, 38), new Rectangle(79, 0, 10, 38), GraphicsUnit.Pixel);
            e.Graphics.DrawImage(Properties.Resources.Notification, new Rectangle(Width - 10, Height - 9, 10, 9), new Rectangle(80, 39, 10, 9), GraphicsUnit.Pixel);

            //Draw Left and Right Edges?
            if(ExtraY > 0)
            {
                //Draw
                LeftBrush.ResetTransform();
                e.Graphics.FillRectangle(LeftBrush, new Rectangle(0, 37, 10, ExtraY));
                RightBrush.ResetTransform();
                e.Graphics.FillRectangle(RightBrush, new Rectangle(Width - 11, 37, 11, ExtraY));
            }

            //Draw Top and Bottom Edges?
            if (ExtraX > 0)
            {
                //Draw
                TopBrush.ResetTransform();
                e.Graphics.FillRectangle(TopBrush, new Rectangle(78, 0, ExtraX, 37));
                BottomBrush.ResetTransform();
                e.Graphics.FillRectangle(BottomBrush, new Rectangle(78, Height - 9, ExtraX, 9));
            }

            //Draw Center?
            if (ExtraX > 0 || ExtraY > 0)
            {
                //Draw
                CenterBrush.ResetTransform();
                e.Graphics.FillRectangle(CenterBrush, new Rectangle(78, 37, ExtraX, ExtraY));
            }
        }
        private void AbideNotification_Shown(object sender, EventArgs e)
        {
            //Refresh
            Refresh();

            //Set Location
            startF = this.Location;
            changeF = PointF.Empty;

            //Start
            timer.Start();

            //Start Loop
            ThreadPool.QueueUserWorkItem(loop);
        }
        private void AbideNotification_FormClosing(object sender, FormClosingEventArgs e)
        {
            //Stop?
            if (!e.Cancel)
                timer.Stop();

            //Set Closing
            closing = !e.Cancel;
        }
        private void timer_Tick(object sender, EventArgs e)
        {
            //Set Location
            float X = startF.X + changeF.X;
            float Y = startF.Y + changeF.Y;
            this.Location = new Point((int)X, (int)Y);
        }
        private void AbideNotification_Click(object sender, EventArgs e)
        {
            close = true;
        }

        //Thread Events
        private void loop(object state)
        {
            //Initialize Volatiles
            startF = this.Location;

            //Initialize Timings
            DateTime Start = DateTime.Now;
            DateTime PreviousTime = DateTime.Now;
            Thread.Sleep(1);
            TimeSpan Ellapsedtime = DateTime.Now - PreviousTime;
            TimeSpan TotalTime = DateTime.Now - Start;

            while (!closing && !close && !IsDisposed)
            {
                //Update Previous
                PreviousTime += Ellapsedtime;

                //Sleep
                Thread.Sleep(1);

                //Update
                Update(Start, Ellapsedtime, TotalTime);

                //Update Timings
                Ellapsedtime = DateTime.Now - PreviousTime;
                TotalTime = DateTime.Now - Start;
            }

            //Exit
            if (!IsDisposed)
                Invoke(new MethodInvoker(delegate { Close(); }));
        }
        private void Update(DateTime Start, TimeSpan Ellapsedtime, TimeSpan TotalTime)
        {
            //Get Variables
            float X = 0f, Y = 0f;
            float Width = Size.Width;
            float Height = Size.Height;

            //Get Timing...
            float inTime = this.transitionIn;
            float hangTime = this.transitionIn + this.hangTime;
            float outTime = this.transitionIn + this.hangTime + this.transitionOut;
            float ellapsedMillis = (float)Ellapsedtime.TotalMilliseconds;
            float totalMillis = (float)TotalTime.TotalMilliseconds;
            float Percent = 1f;

            //Calculate Percentage
            if (totalMillis < inTime)
                Percent = totalMillis / this.transitionIn;
            else if (totalMillis > hangTime)
                Percent = 1 - ((totalMillis - hangTime) / (this.transitionOut));
            else if (totalMillis > outTime) close = true;

            //Set Y Value
            Y = -Height * Percent;

            //Set Location
            changeF = new PointF(X, Y);
        }
    }
}
